"""
自定义断言

提供专门的断言函数，用于验证复杂的数据结构和业务逻辑。
"""

import json
from typing import Dict, Any, List, Optional, Union
from pathlib import Path
from datetime import datetime


def assert_video_equal(actual, expected, ignore_fields=None):
    """断言视频对象相等"""
    ignore_fields = ignore_fields or ['id', 'created_at', 'updated_at']
    
    if hasattr(expected, '__dict__'):
        # 如果是对象，转换为字典
        expected_dict = {k: v for k, v in expected.__dict__.items() 
                        if not k.startswith('_')}
    else:
        expected_dict = expected
    
    if hasattr(actual, '__dict__'):
        actual_dict = {k: v for k, v in actual.__dict__.items() 
                      if not k.startswith('_')}
    else:
        actual_dict = actual
    
    for field, expected_value in expected_dict.items():
        if field in ignore_fields:
            continue
            
        assert field in actual_dict, f"字段 '{field}' 在实际对象中不存在"
        actual_value = actual_dict[field]
        
        if isinstance(expected_value, float):
            assert abs(actual_value - expected_value) < 0.001, \
                f"字段 '{field}' 不匹配: 期望 {expected_value}, 实际 {actual_value}"
        else:
            assert actual_value == expected_value, \
                f"字段 '{field}' 不匹配: 期望 {expected_value}, 实际 {actual_value}"


def assert_project_equal(actual, expected, ignore_fields=None):
    """断言项目对象相等"""
    ignore_fields = ignore_fields or ['id', 'created_at', 'updated_at']
    
    # 处理settings字段的特殊比较
    if 'settings' in expected and 'settings' in actual:
        assert_dict_equal(actual['settings'], expected['settings'], 
                         path='settings')
        ignore_fields = ignore_fields + ['settings']
    
    assert_video_equal(actual, expected, ignore_fields)


def assert_workflow_equal(actual, expected, ignore_fields=None):
    """断言工作流对象相等"""
    ignore_fields = ignore_fields or ['workflow_id', 'created_at', 'updated_at']
    
    # 特殊处理nodes和connections
    if 'nodes' in expected:
        assert 'nodes' in actual, "实际对象中缺少nodes字段"
        assert_dict_equal(actual['nodes'], expected['nodes'], path='nodes')
        ignore_fields = ignore_fields + ['nodes']
    
    if 'connections' in expected:
        assert 'connections' in actual, "实际对象中缺少connections字段"
        assert_list_equal(actual['connections'], expected['connections'], 
                         path='connections')
        ignore_fields = ignore_fields + ['connections']
    
    assert_video_equal(actual, expected, ignore_fields)


def assert_classification_equal(actual, expected, ignore_fields=None):
    """断言分类结果相等"""
    ignore_fields = ignore_fields or ['id', 'created_at']
    
    # 特殊处理confidence字段（浮点数比较）
    if 'confidence' in expected:
        assert 'confidence' in actual, "实际对象中缺少confidence字段"
        assert abs(actual['confidence'] - expected['confidence']) < 0.001, \
            f"confidence不匹配: 期望 {expected['confidence']}, 实际 {actual['confidence']}"
        ignore_fields = ignore_fields + ['confidence']
    
    assert_video_equal(actual, expected, ignore_fields)


def assert_dict_equal(actual: Dict[str, Any], expected: Dict[str, Any], 
                     path: str = "root"):
    """递归断言字典相等"""
    assert isinstance(actual, dict), f"在路径 '{path}': 期望字典，实际 {type(actual)}"
    
    # 检查所有期望的键都存在
    for key, expected_value in expected.items():
        current_path = f"{path}.{key}"
        assert key in actual, f"在路径 '{current_path}': 键不存在"
        
        actual_value = actual[key]
        
        if isinstance(expected_value, dict):
            assert_dict_equal(actual_value, expected_value, current_path)
        elif isinstance(expected_value, list):
            assert_list_equal(actual_value, expected_value, current_path)
        elif isinstance(expected_value, float):
            assert abs(actual_value - expected_value) < 0.001, \
                f"在路径 '{current_path}': 期望 {expected_value}, 实际 {actual_value}"
        else:
            assert actual_value == expected_value, \
                f"在路径 '{current_path}': 期望 {expected_value}, 实际 {actual_value}"


def assert_list_equal(actual: List[Any], expected: List[Any], 
                     path: str = "root"):
    """递归断言列表相等"""
    assert isinstance(actual, list), f"在路径 '{path}': 期望列表，实际 {type(actual)}"
    assert len(actual) == len(expected), \
        f"在路径 '{path}': 列表长度不匹配，期望 {len(expected)}, 实际 {len(actual)}"
    
    for i, (actual_item, expected_item) in enumerate(zip(actual, expected)):
        current_path = f"{path}[{i}]"
        
        if isinstance(expected_item, dict):
            assert_dict_equal(actual_item, expected_item, current_path)
        elif isinstance(expected_item, list):
            assert_list_equal(actual_item, expected_item, current_path)
        elif isinstance(expected_item, float):
            assert abs(actual_item - expected_item) < 0.001, \
                f"在路径 '{current_path}': 期望 {expected_item}, 实际 {actual_item}"
        else:
            assert actual_item == expected_item, \
                f"在路径 '{current_path}': 期望 {expected_item}, 实际 {actual_item}"


def assert_file_exists(file_path: Union[str, Path], message: str = None):
    """断言文件存在"""
    path = Path(file_path)
    message = message or f"文件不存在: {file_path}"
    assert path.exists(), message
    assert path.is_file(), f"路径存在但不是文件: {file_path}"


def assert_directory_exists(dir_path: Union[str, Path], message: str = None):
    """断言目录存在"""
    path = Path(dir_path)
    message = message or f"目录不存在: {dir_path}"
    assert path.exists(), message
    assert path.is_dir(), f"路径存在但不是目录: {dir_path}"


def assert_file_not_exists(file_path: Union[str, Path], message: str = None):
    """断言文件不存在"""
    path = Path(file_path)
    message = message or f"文件不应该存在: {file_path}"
    assert not path.exists(), message


def assert_file_size_greater_than(file_path: Union[str, Path], min_size: int):
    """断言文件大小大于指定值"""
    path = Path(file_path)
    assert_file_exists(path)
    actual_size = path.stat().st_size
    assert actual_size > min_size, \
        f"文件大小不足: 期望 > {min_size}, 实际 {actual_size}"


def assert_file_content_contains(file_path: Union[str, Path], content: str):
    """断言文件内容包含指定字符串"""
    path = Path(file_path)
    assert_file_exists(path)
    file_content = path.read_text(encoding='utf-8')
    assert content in file_content, \
        f"文件内容不包含期望的字符串: '{content}'"


def assert_json_structure(data: Union[str, Dict[str, Any]], 
                         expected_structure: Dict[str, Any]):
    """断言JSON结构符合期望"""
    if isinstance(data, str):
        try:
            data = json.loads(data)
        except json.JSONDecodeError as e:
            assert False, f"无效的JSON格式: {e}"
    
    assert isinstance(data, dict), f"期望字典，实际 {type(data)}"
    
    def check_structure(actual, expected, path="root"):
        for key, expected_type in expected.items():
            current_path = f"{path}.{key}"
            assert key in actual, f"在路径 '{current_path}': 键不存在"
            
            actual_value = actual[key]
            
            if isinstance(expected_type, dict):
                assert isinstance(actual_value, dict), \
                    f"在路径 '{current_path}': 期望字典，实际 {type(actual_value)}"
                check_structure(actual_value, expected_type, current_path)
            elif isinstance(expected_type, list):
                assert isinstance(actual_value, list), \
                    f"在路径 '{current_path}': 期望列表，实际 {type(actual_value)}"
                if expected_type and len(actual_value) > 0:
                    # 检查列表第一个元素的结构
                    check_structure(actual_value[0], expected_type[0], 
                                  f"{current_path}[0]")
            elif expected_type == 'any':
                # 任意类型，不检查
                pass
            else:
                assert isinstance(actual_value, expected_type), \
                    f"在路径 '{current_path}': 期望 {expected_type}, 实际 {type(actual_value)}"
    
    check_structure(data, expected_structure)


def assert_datetime_recent(dt: datetime, max_seconds_ago: int = 60):
    """断言日期时间是最近的"""
    now = datetime.now()
    diff = (now - dt).total_seconds()
    assert diff >= 0, f"日期时间在未来: {dt}"
    assert diff <= max_seconds_ago, \
        f"日期时间太久远: {dt}, 距离现在 {diff} 秒"


def assert_duration_in_range(actual_duration: float, 
                           expected_duration: float, 
                           tolerance_percent: float = 5.0):
    """断言时长在期望范围内"""
    tolerance = expected_duration * (tolerance_percent / 100.0)
    min_duration = expected_duration - tolerance
    max_duration = expected_duration + tolerance
    
    assert min_duration <= actual_duration <= max_duration, \
        f"时长超出范围: 期望 {expected_duration}±{tolerance_percent}%, " \
        f"实际 {actual_duration}"


def assert_video_resolution(width: int, height: int, 
                          expected_resolution: str):
    """断言视频分辨率"""
    resolution_map = {
        '480p': (854, 480),
        '720p': (1280, 720),
        '1080p': (1920, 1080),
        '1440p': (2560, 1440),
        '4k': (3840, 2160),
        '8k': (7680, 4320)
    }
    
    expected_width, expected_height = resolution_map.get(
        expected_resolution.lower(), (width, height))
    
    assert width == expected_width and height == expected_height, \
        f"分辨率不匹配: 期望 {expected_width}x{expected_height}, " \
        f"实际 {width}x{height}"


def assert_confidence_in_range(confidence: float, 
                              min_confidence: float = 0.0,
                              max_confidence: float = 1.0):
    """断言置信度在有效范围内"""
    assert min_confidence <= confidence <= max_confidence, \
        f"置信度超出范围: 期望 [{min_confidence}, {max_confidence}], " \
        f"实际 {confidence}"


def assert_processing_time_reasonable(processing_time: float,
                                    max_time: float = 60.0):
    """断言处理时间合理"""
    assert processing_time > 0, f"处理时间应该大于0: {processing_time}"
    assert processing_time <= max_time, \
        f"处理时间过长: 期望 <= {max_time}, 实际 {processing_time}"


def assert_workflow_execution_successful(execution_result: Dict[str, Any]):
    """断言工作流执行成功"""
    assert 'status' in execution_result, "执行结果缺少status字段"
    assert execution_result['status'] in ['completed', 'success'], \
        f"工作流执行失败: {execution_result.get('status')}"
    
    assert 'results' in execution_result, "执行结果缺少results字段"
    assert isinstance(execution_result['results'], dict), \
        "results字段应该是字典"
    
    if 'error_count' in execution_result:
        assert execution_result['error_count'] == 0, \
            f"工作流执行有错误: {execution_result['error_count']} 个错误"


def assert_classification_result_valid(classification: Dict[str, Any]):
    """断言分类结果有效"""
    required_fields = ['category', 'confidence']
    for field in required_fields:
        assert field in classification, f"分类结果缺少必需字段: {field}"
    
    assert_confidence_in_range(classification['confidence'])
    
    # 检查分类类别是否有效
    valid_categories = [
        'product_showcase', 'model_wearing', 'product_usage',
        'comparison', 'tutorial', 'entertainment', 'advertisement', 'review'
    ]
    
    assert classification['category'] in valid_categories, \
        f"无效的分类类别: {classification['category']}"


def assert_video_metadata_complete(metadata: Dict[str, Any]):
    """断言视频元数据完整"""
    required_fields = ['duration', 'width', 'height', 'format']
    for field in required_fields:
        assert field in metadata, f"视频元数据缺少必需字段: {field}"
    
    assert metadata['duration'] > 0, "视频时长应该大于0"
    assert metadata['width'] > 0, "视频宽度应该大于0"
    assert metadata['height'] > 0, "视频高度应该大于0"
    assert metadata['format'], "视频格式不能为空"


# ============================================================================
# 便捷断言组合
# ============================================================================

def assert_video_processing_result(result: Dict[str, Any]):
    """断言视频处理结果完整且有效"""
    assert 'success' in result, "处理结果缺少success字段"
    assert result['success'] is True, "视频处理失败"
    
    if 'metadata' in result:
        assert_video_metadata_complete(result['metadata'])
    
    if 'classification' in result:
        assert_classification_result_valid(result['classification'])
    
    if 'processing_time' in result:
        assert_processing_time_reasonable(result['processing_time'])


def assert_batch_processing_result(results: List[Dict[str, Any]]):
    """断言批量处理结果"""
    assert isinstance(results, list), "批量处理结果应该是列表"
    assert len(results) > 0, "批量处理结果不能为空"
    
    for i, result in enumerate(results):
        try:
            assert_video_processing_result(result)
        except AssertionError as e:
            assert False, f"第 {i} 个处理结果无效: {e}"
